package vnetlab.virtualbox;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.log4j.*;
import vnetlab.manager.Configuration;
import vnetlab.manager.Manager;
import vnetlab.manager.Status;
import vnetlab.elements.Element;
import vnetlab.xml.XmlManager;

public class VirtualBoxManager {

    private String pathTemplate;
    private String pathVBoxManage;
    private static VirtualBoxManager instance;

    private VirtualBoxManager() {
        this(Configuration.getInstance().getTemplatesPath(), Configuration.getInstance().getVBoxManagePath());
    }

    private VirtualBoxManager(String path, String pathVBoxManage) {
        this.pathTemplate = path;
        this.pathVBoxManage = pathVBoxManage;
    }

    public static VirtualBoxManager getInstance() {
        if (instance == null) {
            instance = new VirtualBoxManager();
        }

        return instance;
    }

   public boolean create(final String nameVM, final String template, int nroInterfaces) throws InterruptedException {
//        try {
//
//            //Importa a maquina virtual
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator+"VBoxManage import " + pathTemplate + File.separator + template + " --vsys 0 --vmname " + nameVM + " --vsys 0");
//            String line;
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null);
//            input.close();
//
//            //Modifica o tamanho da memória desta maquina virtual
//            p = Runtime.getRuntime().exec(pathVBoxManage + File.separator+"VBoxManage modifyvm " + nameVM + " --memory 64 ");
//            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//
//            //Cria as interface de rede da máquina virtual
//            for (int id = 1; id <= nroInterfaces; id++) {
//                p = Runtime.getRuntime().exec(pathVBoxManage + File.separator+"VBoxManage modifyvm " + nameVM + " --nic" + id + " intnet --intnet" + id + " null --macaddress" + id + " auto --nictype"+id+" Am79C973 --cableconnected" + id + " off");
//                input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//                while ((line = input.readLine()) != null) ;
//            }
//            input.close();
//
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }

       //Importa a máquina virtual
       if(executeVBoxManageCommand("import " + pathTemplate + File.separator + template + " --vsys 0 --vmname " + nameVM + " --vsys 0")){

           //Estabele a quantidade de memoria RAM utilizada
           if(executeVBoxManageCommand("modifyvm " + nameVM + " --memory 64 ")){

                //Cria as interfaces de rede da máquina virtual
                for (int id = 1; id <= nroInterfaces; id++) {
                    executeVBoxManageCommand("modifyvm " + nameVM + " --nic" + id + " intnet --intnet" + id + " null --macaddress" + id + " auto --nictype"+id+" Am79C973 --cableconnected" + id + " off");
                }

                return true;
           }
       }
       return false;
    }

    public boolean run(String nameVM) {
//        try {
//
//            //Registra a VM no virtualbox
//            if(registerVm(nameVM)){
//
//                //Executa o VBoxHeadless
//                Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator+ "VBoxHeadless --startvm " + nameVM);
//
//            }
//
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }


            unregisterVm(nameVM);
            //Registra a VM caso ela não tenha sido registrada ainda
            registerVm(nameVM);

            //Inicia a execução da máquina virtual
            if(executeVBoxHeadlessCommand(" --startvm " + nameVM)){
                return true;
            }
            
            return false;
    }


    public boolean stop(String nameVM) {
//        try {
//
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage controlvm " + nameVM + " poweroff");
//            String line;
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null);
//            input.close();
//
//            unregisterVm(nameVM);
//
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }

        if(executeVBoxManageCommand("controlvm " + nameVM + " poweroff")){
            if(unregisterVm(nameVM)){
                return true;
            }
        }
        return false;
    }


    public boolean show(String nameVM) {
//        try {
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage controlvm " + nameVM + " savestate");
//            String line = null;
//
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//
//            Thread.sleep(1000);
//
//            p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage startvm " + nameVM);
//            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//
//        } catch (InterruptedException ex) {
//            java.util.logging.Logger.getLogger(VirtualBoxManager.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }

        //Salva o estado atual da maquina virtual
        if(executeVBoxManageCommand("controlvm " + nameVM + " savestate")){

            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                java.util.logging.Logger.getLogger(VirtualBoxManager.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            }
            //Inicia a GUI 
            if(executeVBoxManageCommand("startvm " + nameVM)){
                return true;
            }
        }
        return false;
    }

    public boolean hide(String nameVM) {
//        try {
//            String line;
//
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage controlvm " + nameVM + " savestate");
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//
//            Thread.sleep(1000);
//
//            Runtime.getRuntime().exec(pathVBoxManage  + File.separator+ "VBoxHeadless --startvm " + nameVM);
//
//        } catch (InterruptedException ex) {
//            java.util.logging.Logger.getLogger(VirtualBoxManager.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }
        if(executeVBoxManageCommand("controlvm " + nameVM + " savestate")){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                java.util.logging.Logger.getLogger(VirtualBoxManager.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
            }
            //Inicia a GUI
            if(executeVBoxHeadlessCommand(" --startvm " + nameVM)){
                return true;
            }
        }
        return false;
        
    }

    // pause
    public boolean pause(String nameVM) {
//        try {
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage controlvm " + nameVM + " pause");
//            String line;
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }

        return executeVBoxManageCommand("controlvm " + nameVM + " pause");
    }

    // resume
    public boolean resume(String nameVM) {
//        try {
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage controlvm " + nameVM + " resume");
//            String line;
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }
        return executeVBoxManageCommand("controlvm " + nameVM + " resume");
    }

    public boolean connectVM2Switch(String nameVM, String nameSwitch, int id) {
//        try {
//            String line;
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage modifyvm " + nameVM + " --intnet" + id + " " + nameSwitch + " --cableconnected" + id + " on");
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }
        return executeVBoxManageCommand("modifyvm " + nameVM + " --intnet" + id + " " + nameSwitch + " --cableconnected" + id + " on");
    }

    public boolean connectVM2VM(String nameVM, String idNetwork, int id) {
//        try {
//            String line;
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage modifyvm " + nameVM + " --nic" + id + " intnet --intnet" + id + " betweenVMs" + idNetwork + " --cableconnected" + id + " on");
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }
        return executeVBoxManageCommand("modifyvm " + nameVM + " --nic" + id + " intnet --intnet" + id + " betweenVMs" + idNetwork + " --cableconnected" + id + " on");
    }

    public boolean removeNetwork(String name, int id) {
//        try {
//            String line;
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage modifyvm " + name + " --cableconnected" + id + " off");
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }
        return executeVBoxManageCommand("modifyvm " + name + " --cableconnected" + id + " off");
    }

    // falta implementar
    public boolean removeVM(String name) {
        try {
            //excluir hd
            String line, nameIDE, os, nameHD,
                    comDel = "";
            String[] aux1 = null,aux= null;
            os = System.getProperty("os.name").toLowerCase();
            if (os.indexOf("win") >= 0) {
                comDel = "del";

            } else if (os.indexOf("nux") >= 0) {
                comDel = "rm -f";

            } else {
                System.out.println("é mac ou outro");
            }



            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage showvminfo " + name);

            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
            while ((line = input.readLine()) != null) {
                if(line.contains("Storage Controller Name")){
                aux = line.split(":");
                }
                if(line.contains("(0, 0)")){
                aux1 = line.split("UUID:");
                }
            }
            input.close();

            for(int i=0;i<aux[1].length();i++){
                if(aux[1].charAt(0) == ' ')
                    aux[1]= aux[1].substring(1);
                    nameIDE = aux[1];
            }

            nameHD= aux1[1].substring(1,aux1[1].length()-1);

//            p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage storageattach " + name + " --storagectl \"" + aux[1] + "\"  --port 0 --device 0 --medium none");
//            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
            
//            p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage storageattach " + name + " --storagectl \"" + aux[1] + "\"  --port 1 --device 0 --medium none");
//            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();

            //excluir VM
//            p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage unregistervm " + name + " --delete");
//            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();

//            p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage closemedium disk " + nameHD + " --delete");
//            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();

            if(executeVBoxManageCommand("storageattach " + name + " --storagectl \"" + aux[1] + "\"  --port 0 --device 0 --medium none")){

                if(executeVBoxManageCommand(" storageattach " + name + " --storagectl \"" + aux[1] + "\"  --port 1 --device 0 --medium none")){

                    if(executeVBoxManageCommand("closemedium disk " + nameHD + " --delete" )){

                        if(executeVBoxManageCommand("closemedium disk " + nameHD + " --delete")){
                            return true;
                        }
                    }
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
        }
        return false;
    }


    public boolean modifyVirtualBoxFolders(){
//        try {
//            String line = null;
//            Process p = Runtime.getRuntime().exec(pathVBoxManage +File.separator+"VBoxManage setproperty hdfolder " + Configuration.getInstance().getVmPath());
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//
//            p = Runtime.getRuntime().exec(pathVBoxManage +File.separator+"VBoxManage setproperty machinefolder "+Configuration.getInstance().getVmPath());
//            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }

        XmlManager.enableVirtualBoxXmlFile(false);
        if(executeVBoxManageCommand("setproperty hdfolder " + Configuration.getInstance().getVmPath())){

            if(executeVBoxManageCommand(" setproperty machinefolder "+Configuration.getInstance().getVmPath())){

                
                return true;
            }
        }
        return false;
    }

    public boolean cameBackDefaultFolders(){
//        try {
//            String line = null;
//            Process p = Runtime.getRuntime().exec(pathVBoxManage +File.separator+"VBoxManage setproperty hdfolder default");
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//
//            p = Runtime.getRuntime().exec(pathVBoxManage +File.separator+"VBoxManage setproperty machinefolder default");
//            input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//        } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }

        
        if(executeVBoxManageCommand("setproperty hdfolder default")){
            if(executeVBoxManageCommand(" setproperty machinefolder default")){
                XmlManager.enableVirtualBoxXmlFile(true);
                return true;
            }
        }
        return false;
    }

    public void stopAll() {
        for (Element element : Manager.getInstance().getMap().values()) {
            if (element != null && element.isCreated() && !element.getType().equals("Switch")) {
                stop(element.getName());
                element.setStatus(Status.STOP);
                element.setTerminalOpened(false);
            }
        }

        cameBackDefaultFolders();
    }


    public boolean registerVm(String nameVM){
//        try {
//
//            String path = Configuration.getInstance().getVmPath();
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage registervm  " +path + File.separator + nameVM  );
//            String line = null;
//
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//
//       } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }
        String path = Configuration.getInstance().getVmPath();
        return executeVBoxManageCommand("registervm  " +path + File.separator + nameVM + File.separator + nameVM+".xml"  );
    }

    public boolean unregisterVm(String nameVM){
//       try {
//            Process p = Runtime.getRuntime().exec(pathVBoxManage + File.separator + "VBoxManage unregistervm " + nameVM );
//            String line = null;
//
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) ;
//            input.close();
//
//       } catch (IOException ex) {
//            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
//        }
        return executeVBoxManageCommand("unregistervm " + nameVM);
    }


    private boolean executeVBoxManageCommand(String command){
       try {

            command = pathVBoxManage + File.separator + "VBoxManage " + command;

            System.out.println("\n\n");
            System.out.println(command);
            
            Process p = Runtime.getRuntime().exec(command);
            String line = null;
            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
            while ((line = input.readLine()) != null) {
               System.out.println(line);
               if(line.contains("ERROR:"))
                   return false;
            }
            input.close();

            System.out.println("\n\n");

            return true;

       } catch (IOException ex) {
            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
            return false;
       }
    }

    private boolean executeVBoxHeadlessCommand(String command){
          try {

            command = pathVBoxManage + File.separator + "VBoxHeadless " + command;

            System.out.println("\n\n");
            System.out.println(command);

            Runtime.getRuntime().exec(command);
//            Process p = Runtime.getRuntime().exec(command);
//            String line = null;
//            BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
//            while ((line = input.readLine()) != null) {
//               System.out.println(line);
//               if(line.contains("ERROR:"))
//                   return false;
//            }
//            input.close();

            System.out.println("\n\n");
            return true;

       } catch (IOException ex) {
            Logger.getLogger(VirtualBoxManager.class.getName()).log(Level.ERROR, null, ex);
            return false;
       }
    }

}
